/**
 * Copyright (c) 2009-2010 Thales Corporate Services S.A.S.
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-v2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 * 
 * Contributors:
 * Thales Corporate Services S.A.S - initial API and implementation
 */
package org.eclipse.egf.core.platform;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.Platform;
import org.eclipse.egf.common.activator.EGFAbstractPlugin;
import org.eclipse.egf.common.l10n.EGFCommonMessages;
import org.eclipse.egf.core.platform.internal.pde.EgfPlatformManager;
import org.eclipse.egf.core.platform.internal.pde.IManagerConstants;
import org.eclipse.egf.core.platform.pde.IPlatformBundle;
import org.eclipse.egf.core.platform.pde.IPlatformExtensionPoint;
import org.eclipse.egf.core.platform.pde.IPlatformExtensionPointFactory;
import org.eclipse.egf.core.platform.pde.IPlatformManager;
import org.eclipse.egf.core.platform.uri.EgfURIConverter;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.BundleContext;

/**
 * @author Xavier Maysonnave
 * 
 */
public class EGFPlatformPlugin extends EGFAbstractPlugin {

	private static EGFPlatformPlugin __plugin;

	private IPlatformManager platformManager;

	private EgfURIConverter uriConverter;

	private static Map<String, IConfigurationElement> __managerExtensionPoints;

	private static Map<String, Class<? extends IPlatformExtensionPoint>> __platformExtensionPoints;

	private static Collection<Class<? extends IPlatformExtensionPoint>> __platformExtensionPointClasses;

	private static Map<Class<? extends IPlatformExtensionPoint>, IPlatformExtensionPointFactory<? extends IPlatformExtensionPoint>> __platformExtensionPointFactories;

	/**
	 * Returns the shared instance
	 * 
	 * @return the shared instance
	 */
	public static EGFPlatformPlugin getDefault() {
		return __plugin;
	}

	public static IPlatformManager getPlatformManager() {
		return getDefault().platformManager;
	}

	/**
	 * Returns a snapshot of known IPlatformFcore
	 * 
	 * @return an array of IPlatformFcore
	 */
	public static EgfURIConverter getPlatformURIConverter() {
		return getDefault().uriConverter;
	}

	/**
	 * Get __managerExtensionPoints.
	 * 
	 * @return an empty map if none could be found.
	 */
	public Map<String, IConfigurationElement> getManagerExtensionPoints() {
		// Lazy loading.
		if (__managerExtensionPoints == null) {
			// Build managers map
			Map<String, IConfigurationElement> platformManagers = new HashMap<String, IConfigurationElement>();
			Map<String, Class<? extends IPlatformExtensionPoint>> interfaces = new HashMap<String, Class<? extends IPlatformExtensionPoint>>();
			IConfigurationElement[] elements = Platform.getExtensionRegistry().getConfigurationElementsFor(getDefault().getBundle().getSymbolicName(), IManagerConstants.MANAGER_EXTENSION_POINT_ID);
			// Get EGF Platform extension points.
			for (IConfigurationElement element : elements) {
				// Extension retrieval
				String extension = element.getAttribute(IManagerConstants.MANAGER_ATT_EXTENSION);
				// Ignore
				if (extension == null || extension.trim().length() == 0) {
					continue;
				}
				extension = extension.trim();
				// Check
				if (platformManagers.containsKey(extension)) {
					getDefault().logError(NLS.bind(EGFCommonMessages.Duplicate_Element_Message, extension));
					getDefault().logInfo(NLS.bind(EGFCommonMessages.Bundle_Message, element.getContributor().getName()), 1);
					getDefault().logInfo(NLS.bind(EGFCommonMessages.Extension_Point_Message, element.getName()), 1);
					continue;
				}
				// Time to init Factory
				long startTimeFactory = System.currentTimeMillis();
				// Check factory
				Object factory = null;
				try {
					factory = element.createExecutableExtension(IManagerConstants.MANAGER_ATT_CLASS);
				} catch (Throwable t) {
					getDefault().logError(t);
				}
				if (EGFPlatformPlugin.getDefault().isDebugging()) {
					long endTime = System.currentTimeMillis();
					long time = (endTime - startTimeFactory);
					EGFPlatformPlugin.getDefault().logInfo(NLS.bind("EGFPlatformPlugin _ create factory ''{0}'' in ''{1}'' ms", //$NON-NLS-1$ 
							element.getAttribute(IManagerConstants.MANAGER_ATT_EXTENSION), time));
				}
				if (factory == null) {
					continue;
				}
				if (factory instanceof IPlatformExtensionPointFactory<?> == false) {
					getDefault().logError(NLS.bind(EGFCommonMessages.Wrong_Class_Message, factory.getClass().getName()));
					getDefault().logInfo(NLS.bind(EGFCommonMessages.Wrong_Class_Sub_Type_Message, IPlatformExtensionPointFactory.class.getName()), 1);
					getDefault().logInfo(NLS.bind(EGFCommonMessages.Bundle_Message, element.getContributor().getName()), 1);
					getDefault().logInfo(NLS.bind(EGFCommonMessages.Extension_Point_Message, element.getName()), 1);
					getDefault().logInfo(NLS.bind(EGFCommonMessages.Attribute_Message, extension), 1);
					continue;
				}
				// Fetch Returned Types from Class
				Class<? extends IPlatformExtensionPoint> clazz = fetchReturnedTypeFromFactory(factory.getClass());
				if (clazz == null) {
					getDefault().logError(NLS.bind(EGFCommonMessages.Wrong_Class_Message, factory.getClass().getName()));
					getDefault().logInfo("Unable to find ''createExtensionPoint(IPlatformBundle platformBundle, Object object)'' method."); //$NON-NLS-1$
					getDefault().logInfo(NLS.bind(EGFCommonMessages.Bundle_Message, element.getContributor().getName()), 1);
					getDefault().logInfo(NLS.bind(EGFCommonMessages.Extension_Point_Message, element.getName()), 1);
					continue;
				}
				if (interfaces.get(factory.getClass().getName()) != null) {
					getDefault().logError(NLS.bind(EGFCommonMessages.Duplicate_Element_Message, factory.getClass().getName()));
					getDefault().logInfo(NLS.bind(EGFCommonMessages.Bundle_Message, element.getContributor().getName()), 1);
					getDefault().logInfo(NLS.bind(EGFCommonMessages.Extension_Point_Message, element.getName()), 1);
					continue;
				}
				// Register
				platformManagers.put(extension, element);
				interfaces.put(factory.getClass().getName(), clazz);
			}
			// Assign static map
			__managerExtensionPoints = platformManagers;
		}
		return __managerExtensionPoints;
	}

	@SuppressWarnings("unchecked")
	public static Map<String, Class<? extends IPlatformExtensionPoint>> getPlatformExtensionPoints() {
		if (__platformExtensionPoints == null) {
			Map<String, IConfigurationElement> platformManagers = getDefault().getManagerExtensionPoints();
			Map<String, Class<? extends IPlatformExtensionPoint>> extensionPoints = new HashMap<String, Class<? extends IPlatformExtensionPoint>>();
			for (Map.Entry<String, IConfigurationElement> entry : platformManagers.entrySet()) {
				// Factory
				IPlatformExtensionPointFactory<?> clazz = null;
				try {
					clazz = (IPlatformExtensionPointFactory<?>) entry.getValue().createExecutableExtension(IManagerConstants.MANAGER_ATT_CLASS);
				} catch (CoreException ce) {
					EGFPlatformPlugin.getDefault().logError(ce);
				}
				if (clazz == null) {
					continue;
				}
				// Fetch Returned Types from Factory
				Class<?> key = EGFPlatformPlugin.fetchReturnedTypeFromFactory(((IPlatformExtensionPointFactory<?>) clazz).getClass());
				// Store it
				extensionPoints.put(entry.getKey(), (Class<? extends IPlatformExtensionPoint>) key);
			}
			__platformExtensionPoints = extensionPoints;
		}
		return __platformExtensionPoints;
	}

	public static Collection<Class<? extends IPlatformExtensionPoint>> getPlatformExtensionPointClasses() {
		if (__platformExtensionPointClasses == null) {
			__platformExtensionPointClasses = getPlatformExtensionPoints().values();
		}
		return __platformExtensionPointClasses;
	}

	@SuppressWarnings("unchecked")
	public static Map<Class<? extends IPlatformExtensionPoint>, IPlatformExtensionPointFactory<? extends IPlatformExtensionPoint>> getPlatformExtensionPointFactories() {
		if (__platformExtensionPointFactories == null) {
			Map<String, IConfigurationElement> platformManagers = EGFPlatformPlugin.getDefault().getManagerExtensionPoints();
			Map<Class<? extends IPlatformExtensionPoint>, IPlatformExtensionPointFactory<? extends IPlatformExtensionPoint>> extensionPointFactories = new HashMap<Class<? extends IPlatformExtensionPoint>, IPlatformExtensionPointFactory<? extends IPlatformExtensionPoint>>();
			for (Map.Entry<String, IConfigurationElement> entry : platformManagers.entrySet()) {
				// Factory
				IPlatformExtensionPointFactory<?> clazz = null;
				try {
					clazz = (IPlatformExtensionPointFactory<?>) entry.getValue().createExecutableExtension(IManagerConstants.MANAGER_ATT_CLASS);
				} catch (CoreException ce) {
					EGFPlatformPlugin.getDefault().logError(ce);
				}
				if (clazz == null) {
					continue;
				}
				// Fetch Returned Types from Factory
				Class<?> key = EGFPlatformPlugin.fetchReturnedTypeFromFactory(((IPlatformExtensionPointFactory<?>) clazz).getClass());
				// Store it
				extensionPointFactories.put((Class<? extends IPlatformExtensionPoint>) key, (IPlatformExtensionPointFactory<?>) clazz);
			}
			__platformExtensionPointFactories = extensionPointFactories;
		}
		return __platformExtensionPointFactories;
	}

	@SuppressWarnings("unchecked")
	public static Class<? extends IPlatformExtensionPoint> fetchReturnedTypeFromFactory(Class<?> factory) {
		Method method = null;
		try {
			method = factory.getDeclaredMethod("createExtensionPoint", IPlatformBundle.class, Object.class); //$NON-NLS-1$
		} catch (NoSuchMethodException nsme) {
			getDefault().logError(nsme);
		}
		if (method == null) {
			return null;
		}
		Type type = null;
		try {
			type = method.getGenericReturnType();
		} catch (Throwable t) {
			getDefault().logError(t);
		}
		if (type == null) {
			return null;
		}
		try {
			return (Class<? extends IPlatformExtensionPoint>) type;
		} catch (ClassCastException cce) {
			getDefault().logError(cce);
		}
		return null;
	}

	/**
	 * The constructor
	 */
	public EGFPlatformPlugin() {
		super();
	}

	/**
	 * 
	 * org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.
	 * BundleContext)
	 */
	@Override
	public void start(BundleContext context) throws Exception {
		super.start(context);
		__plugin = this;
		uriConverter = new EgfURIConverter();
		platformManager = new EgfPlatformManager();
	}

	/**
	 * org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.
	 * BundleContext)
	 */
	@Override
	public void stop(BundleContext context) throws Exception {
		platformManager.dispose();
		platformManager = null;
		uriConverter = null;
		__managerExtensionPoints = null;
		__platformExtensionPoints = null;
		__platformExtensionPointClasses = null;
		__platformExtensionPointFactories = null;
		super.stop(context);
		__plugin = null;
	}

}
